/*
 *  FILE: open.c
 *  AUTH: mcc | jal
 *  DESC:
 *  DATE: Mon Apr  6 19:27:49 1998
 */

#include "globals.h"
#include "errno.h"
#include "fs/fcntl.h"
#include "util/string.h"
#include "util/printf.h"
#include "fs/vfs.h"
#include "fs/vnode.h"
#include "fs/file.h"
#include "fs/vfs_syscall.h"
#include "fs/open.h"
#include "fs/stat.h"
#include "util/debug.h"

/* find empty index in p->p_files[] */
int
get_empty_fd(proc_t *p)
{
        int fd;

        for (fd = 0; fd < NFILES; fd++) {
                if (!p->p_files[fd])
                        return fd;
        }

        dbg(DBG_ERROR | DBG_VFS, "ERROR: get_empty_fd: out of file descriptors "
            "for pid %d\n", curproc->p_pid);
        return -EMFILE;
}

/*
 * There a number of steps to opening a file:
 *      1. Get the next empty file descriptor.
 *      2. Call fget to get a fresh file_t.
 *      3. Save the file_t in curproc's file descriptor table.
 *      4. Set file_t->f_mode to OR of FMODE_(READ|WRITE|APPEND) based on
 *         oflags, which can be O_RDONLY, O_WRONLY or O_RDWR, possibly OR'd with
 *         O_APPEND.
 *      5. Use open_namev() to get the vnode for the file_t.
 *      6. Fill in the fields of the file_t.
 *      7. Return new fd.
 *
 * If anything goes wrong at any point (specifically if the call to open_namev
 * fails), be sure to remove the fd from curproc, fput the file_t and return an
 * error.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EINVAL
 *        oflags is not valid.
 *      o EMFILE
 *        The process already has the maximum number of files open.
 *      o ENOMEM
 *        Insufficient kernel memory was available.
 *      o ENAMETOOLONG
 *        A component of filename was too long.
 *      o ENOENT
 *        O_CREAT is not set and the named file does not exist.  Or, a
 *        directory component in pathname does not exist.
 *      o EISDIR
 *        pathname refers to a directory and the access requested involved
 *        writing (that is, O_WRONLY or O_RDWR is set).
 *      o ENXIO
 *        pathname refers to a device special file and no corresponding device
 *        exists.
 */

static int oflagsExist(int oflags)
{
    return oflags == O_RDONLY || oflags == O_WRONLY || oflags == O_RDWR;
}

int
do_open(const char *filename, int oflags)
{

        /*check oflags*/
        if(oflagsExist(oflags) == -1)
            return -EINVAL;

        /*Get the next empty file descriptor*/
        int fd = get_empty_fd(curproc);
        if(-EMFILE == fd)
            return -EMFILE;


        /*Call fget to get a fresh file_t*/
        file_t* new_file = fget(-1);
    if(new_file==NULL)
        return -ENOMEM;
      
        /*Save the file_t in curproc's file descriptor table.*/
      

        /*Set file_t->f_mode to OR of FMODE_(READ|WRITE|APPEND) based on
 *         oflags, which can be O_RDONLY, O_WRONLY or O_RDWR, possibly OR'd with
 *         O_APPEND.*/
        switch(oflags&0xf)
        {
            case O_RDONLY:
                new_file->f_mode = new_file->f_mode | FMODE_READ;
            break;
            case O_WRONLY:
                new_file->f_mode = new_file->f_mode | FMODE_WRITE;
            break;
            case O_RDWR:
                new_file->f_mode = new_file->f_mode | FMODE_READ | FMODE_WRITE;
            break;
        default: return -EINVAL;

        }

    if (oflags & O_APPEND)
         new_file->f_mode = new_file->f_mode | FMODE_APPEND;

      
        /*open_namev(const char *pathname, int flag, vnode_t **res_vnode, vnode_t *base)*/
        /*base NULL means create a new node in current directory*/
        
    int ret = open_namev(filename, oflags, &new_file->f_vnode, NULL); 
        if(ret != 0)
        {
            fput(new_file);
            return ret;
        }

    if (S_ISDIR(new_file->f_vnode->vn_mode) && (oflags  == O_WRONLY || oflags  == O_RDWR)){
        fput(new_file);
        return -EISDIR;
    }
    if ((S_ISCHR(new_file->f_vnode->vn_mode)) && (new_file->f_vnode->vn_cdev == NULL)) {
        fput(new_file);
        return -ENXIO;
    }
    if ((S_ISBLK(new_file->f_vnode->vn_mode)) && (new_file->f_vnode->vn_bdev == NULL)){
        fput(new_file);
        return -ENXIO;
    }

         curproc->p_files[fd] = new_file;
        return fd;
}

